﻿#include "MaterialOperationNodeViewModel.h"

#include "MaterialGraph/Pin/MaterialGraphPinViewModel.h"
#include "MaterialGraph/MaterialGraphViewModel.h"


void RMaterialOperationNodeViewModel::BindModel(TSharedObjectPtr<RMaterialGraphNodeModel> InModel, TSharedObjectPtr<RMaterialGraphViewModel> InGraph)
{
    Super::BindModel(InModel, InGraph);

    OperationModel = InModel;

    //assign line index to pins
    OutputViewModels[0]->SetLineIndex(1);
    InputViewModels[0]->SetLineIndex(4);
    InputViewModels[1]->SetLineIndex(5);
    InputViewModels[2]->SetLineIndex(6);


    //fill popup menu
    {
        auto& FunctionColumn=PopupMenu.Columns.Emplace();
        FunctionColumn.Name=U"函数";
        FunctionColumn.Width=150.0f;
        {
            auto& AddItem=FunctionColumn.Items.Emplace();
            AddItem.OperationType=EMaterialOperationType::Add;
        }

        {
            auto& SubtractItem=FunctionColumn.Items.Emplace();
            SubtractItem.OperationType=EMaterialOperationType::Subtract;
        }

        {
            auto& MultiplyItem=FunctionColumn.Items.Emplace();
            MultiplyItem.OperationType=EMaterialOperationType::Multiply;
        }

        {
            auto& DivideItem=FunctionColumn.Items.Emplace();
            DivideItem.OperationType=EMaterialOperationType::Divide;
        }

        // AddAfterMultiply
        {
            auto& AddAfterMultiplyItem=FunctionColumn.Items.Emplace();
            AddAfterMultiplyItem.OperationType=EMaterialOperationType::AddAfterMultiply;
        }

        // Logarithm
        {
            auto& LogarithmItem=FunctionColumn.Items.Emplace();
            LogarithmItem.OperationType=EMaterialOperationType::Logarithm;
        }

        // SquareRoot
        {
            auto& SquareRootItem=FunctionColumn.Items.Emplace();
            SquareRootItem.OperationType=EMaterialOperationType::SquareRoot;
        }

        // Inverse
        {
            auto& InverseItem=FunctionColumn.Items.Emplace();
            InverseItem.OperationType=EMaterialOperationType::Inverse;
        }

        // InverseSquareRoot
        {
            auto& InverseSquareRootItem=FunctionColumn.Items.Emplace();
            InverseSquareRootItem.OperationType=EMaterialOperationType::InverseSquareRoot;
        }

        // Absolute
        {
            auto& AbsoluteItem=FunctionColumn.Items.Emplace();
            AbsoluteItem.OperationType=EMaterialOperationType::Absolute;
        }

        // Power
        {
            auto& PowerItem=FunctionColumn.Items.Emplace();
            PowerItem.OperationType=EMaterialOperationType::Power;
        }
        
    }

    {
        auto& CompareColumn=PopupMenu.Columns.Emplace();
        CompareColumn.Name=U"比较";
        CompareColumn.Width=150.0f;
        {
            auto& MinItem=CompareColumn.Items.Emplace();
            MinItem.OperationType=EMaterialOperationType::Min;
        }

        {
            auto& MaxItem=CompareColumn.Items.Emplace();
            MaxItem.OperationType=EMaterialOperationType::Max;
        }

        {
            auto& GreaterItem=CompareColumn.Items.Emplace();
            GreaterItem.OperationType=EMaterialOperationType::Greater;
        }

        {
            auto& LessItem=CompareColumn.Items.Emplace();
            LessItem.OperationType=EMaterialOperationType::Less;
        }

        {
            auto& SignItem=CompareColumn.Items.Emplace();
            SignItem.OperationType=EMaterialOperationType::Sign;
        }

        {
            auto& EqualItem=CompareColumn.Items.Emplace();
            EqualItem.OperationType=EMaterialOperationType::Equal;
        }

        {
            auto& SmoothMinItem=CompareColumn.Items.Emplace();
            SmoothMinItem.OperationType=EMaterialOperationType::SmoothMin;
        }

        {
            auto& SmoothMaxItem=CompareColumn.Items.Emplace();
            SmoothMaxItem.OperationType=EMaterialOperationType::SmoothMax;
        }
    }

    {
        auto& RoundColumn=PopupMenu.Columns.Emplace();
        RoundColumn.Name=U"取整";
        RoundColumn.Width=150;

        {
            auto& FloorItem=RoundColumn.Items.Emplace();
            FloorItem.OperationType=EMaterialOperationType::Floor;
        }

        {
            auto& CeilItem=RoundColumn.Items.Emplace();
            CeilItem.OperationType=EMaterialOperationType::Ceil;
        }

        {
            auto& RoundItem=RoundColumn.Items.Emplace();
            RoundItem.OperationType=EMaterialOperationType::Round;
        }

        {
            auto& IntegerItem=RoundColumn.Items.Emplace();
            IntegerItem.OperationType=EMaterialOperationType::Integer;
        }

        {
            auto& FractItem=RoundColumn.Items.Emplace();
            FractItem.OperationType=EMaterialOperationType::Fraction;
        }

        // Snap
        {
            auto& SnapItem=RoundColumn.Items.Emplace();
            SnapItem.OperationType=EMaterialOperationType::Snap;
        }

        // Loop
        {
            auto& LoopItem=RoundColumn.Items.Emplace();
            LoopItem.OperationType=EMaterialOperationType::Loop;
        }

    }


    {
        auto& TrigonometricColumn=PopupMenu.Columns.Emplace();
        TrigonometricColumn.Name=U"三角函数";
        TrigonometricColumn.Width=150;

        {
            auto& SinItem=TrigonometricColumn.Items.Emplace();
            SinItem.OperationType=EMaterialOperationType::Sin;
        }

        {
            auto& CosItem=TrigonometricColumn.Items.Emplace();
            CosItem.OperationType=EMaterialOperationType::Cos;
        }

        {
            auto& TanItem=TrigonometricColumn.Items.Emplace();
            TanItem.OperationType=EMaterialOperationType::Tan;
        }

        {
            auto& AsinItem=TrigonometricColumn.Items.Emplace();
            AsinItem.OperationType=EMaterialOperationType::Asin;
        }

        {
            auto& AcosItem=TrigonometricColumn.Items.Emplace();
            AcosItem.OperationType=EMaterialOperationType::Acos;
        }

        {
            auto& AtanItem=TrigonometricColumn.Items.Emplace();
            AtanItem.OperationType=EMaterialOperationType::Atan;
        }

        {
            auto& Atan2Item=TrigonometricColumn.Items.Emplace();
            Atan2Item.OperationType=EMaterialOperationType::Atan2;
        }

        {
            auto& SinhItem=TrigonometricColumn.Items.Emplace();
            SinhItem.OperationType=EMaterialOperationType::Sinh;
        }

        {
            auto& CoshItem=TrigonometricColumn.Items.Emplace();
            CoshItem.OperationType=EMaterialOperationType::Cosh;
        }

        {
            auto& TanhItem=TrigonometricColumn.Items.Emplace();
            TanhItem.OperationType=EMaterialOperationType::Tanh;
        }
    }

    {
        auto& ConvertColumn=PopupMenu.Columns.Emplace();
        ConvertColumn.Name=U"转换";
        ConvertColumn.Width=150;

        {
            auto& ToRadiansItem=ConvertColumn.Items.Emplace();
            ToRadiansItem.OperationType=EMaterialOperationType::ToRadians;
        }

        {
            auto& ToDegreesItem=ConvertColumn.Items.Emplace();
            ToDegreesItem.OperationType=EMaterialOperationType::ToDegrees;
        }
    }

    //calculate popup menu size
    {
        PopupMenu.Size.X=0;
        PopupMenu.Size.Y=0;
        for(auto& Column:PopupMenu.Columns)
        {
            PopupMenu.Size.X+=Column.Width;
            PopupMenu.Size.Y=CMath::Max(PopupMenu.Size.Y,Column.Items.Num());
        }

        PopupMenu.Size.X+=PopupMenuHorizontalSpacing*(PopupMenu.Columns.Num()-1)+2*PopupMenuPadding;
        PopupMenu.Size.Y=PopupMenu.Size.Y*(PopupMenuVerticalSpacing+ColumnLineHeight)
            +2*PopupMenuPadding 
            + ColumnLineHeight; //title line height
    }

    //fill OperationTypeData
    {
        OperationTypeData.Add(EMaterialOperationType::Add, TVector<CString>({U"相加",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Subtract, TVector<CString>({U"相减",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Multiply, TVector<CString>({U"相乘",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Divide, TVector<CString>({U"相除",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::AddAfterMultiply, TVector<CString>({U"先乘后加",U"数字A",U"乘数",U"加数"}));
        OperationTypeData.Add(EMaterialOperationType::Logarithm, TVector<CString>({U"对数",U"数字", U"底数"}));
        OperationTypeData.Add(EMaterialOperationType::SquareRoot,TVector<CString>( {U"平方根",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Inverse, TVector<CString>({U"倒数",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::InverseSquareRoot, TVector<CString>({U"平方根的倒数",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Absolute, TVector<CString>({U"绝对值",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Power, TVector<CString>({U"指数",U"底数",U"指数"}));
        OperationTypeData.Add(EMaterialOperationType::Min, TVector<CString>({U"最小值",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Max, TVector<CString>({U"最大值",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Greater, TVector<CString>({U"大于",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Less, TVector<CString>({U"小于",U"数字A",U"数字B"}));
        OperationTypeData.Add(EMaterialOperationType::Sign, TVector<CString>({U"符号",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Equal, TVector<CString>({U"等于",U"数字A",U"数字B",U"容差"}));
        OperationTypeData.Add(EMaterialOperationType::SmoothMin, TVector<CString>({U"平滑最小值",U"数字A",U"数字B",U"距离"}));
        OperationTypeData.Add(EMaterialOperationType::SmoothMax,TVector<CString>( {U"平滑最大值",U"数字A",U"数字B",U"距离"}));
        OperationTypeData.Add(EMaterialOperationType::Floor, TVector<CString>({U"向下取整",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Ceil, TVector<CString>({U"向上取整",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Round, TVector<CString>({U"四舍五入",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Integer, TVector<CString>({U"取整数部分",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Fraction,  TVector<CString>({U"取小数部分",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Snap,  TVector<CString>({U"吸附",U"数字",U"步长"}));
        OperationTypeData.Add(EMaterialOperationType::Loop,  TVector<CString>({U"循环",U"数字",U"最小值",U"最大值"}));
        OperationTypeData.Add(EMaterialOperationType::Sin, TVector<CString>({U"正弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Cos,  TVector<CString>({U"余弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Tan,  TVector<CString>({U"正切",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Asin, TVector<CString>({U"反正弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Acos, TVector<CString>( {U"反余弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Atan, TVector<CString>({U"反正切",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Atan2, TVector<CString>({U"反正切2",U"Y",U"X"}));
        OperationTypeData.Add(EMaterialOperationType::Sinh,  TVector<CString>({U"双曲正弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Cosh, TVector<CString>( {U"双曲余弦",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::Tanh, TVector<CString>({U"双曲正切",U"数字"}));
        OperationTypeData.Add(EMaterialOperationType::ToRadians,  TVector<CString>({U"角度转弧度",U"角度"}));
        OperationTypeData.Add(EMaterialOperationType::ToDegrees,  TVector<CString>({U"弧度转角度",U"弧度"}));
    }
}

int64_t RMaterialOperationNodeViewModel::GetLineNum() const
{
    // title + output + combobox + clamp+ inputs
    return 4+ OperationModel->GetValidInputNum();
}

CString RMaterialOperationNodeViewModel::GetOperationName() const
{
    return OperationTypeData[OperationModel->GetOperationType()].DisplayName;
}

bool RMaterialOperationNodeViewModel::IsClampEnable() const
{
    return OperationModel-> bClamp;
}

void RMaterialOperationNodeViewModel::EnableClamp(bool bEnable)
{
    OperationModel->bClamp = bEnable;
}

TSharedObjectPtr<RMaterialGraphPinViewModel> RMaterialOperationNodeViewModel::GetFirstInputPinViewModel() const
{
    return InputViewModels[0];
}

TSharedObjectPtr<RMaterialGraphPinViewModel> RMaterialOperationNodeViewModel::GetSecondInputPinViewModel() const
{
    return InputViewModels[1];
}

TSharedObjectPtr<RMaterialGraphPinViewModel> RMaterialOperationNodeViewModel::GetThirdInputPinViewModel() const
{
    return InputViewModels[2];
}

TSharedObjectPtr<RMaterialGraphPinViewModel> RMaterialOperationNodeViewModel::GetOutputPinViewModel() const
{
    return OutputViewModels[0];
}

bool RMaterialOperationNodeViewModel::IsInputBVisible() const
{
    return OperationModel->GetValidInputNum()>=2;
}

bool RMaterialOperationNodeViewModel::IsInputCVisible() const
{
    return OperationModel->GetValidInputNum()>=3;
}

EMaterialOperationType RMaterialOperationNodeViewModel::GetOperationType() const
{
    return OperationModel->GetOperationType();
}


float RMaterialOperationNodeViewModel::GetPopupMenuWidth() const
{
    return PopupMenu.Size.X;
}

float RMaterialOperationNodeViewModel::GetPopupMenuHeight() const
{
    return PopupMenu.Size.Y;
}

int64_t RMaterialOperationNodeViewModel::GetPopupMenuColumnNum() const
{
    return PopupMenu.Columns.Num();
}

CString RMaterialOperationNodeViewModel::GetPopupMenuColumnName(int64_t ColumnIndex) const
{
    return PopupMenu.Columns[ColumnIndex].Name;
}

float RMaterialOperationNodeViewModel::GetPopupMenuColumnWidth(int64_t ColumnIndex) const
{
    return PopupMenu.Columns[ColumnIndex].Width;
}

int64_t RMaterialOperationNodeViewModel::GetPopupMenuLineNum(int64_t ColumnIndex) const
{
    return PopupMenu.Columns[ColumnIndex].Items.Num();
}

CString RMaterialOperationNodeViewModel::GetPopupMenuItemName(int64_t ColumnIndex, int64_t LineIndex) const
{
    auto OperationType=PopupMenu.Columns[ColumnIndex].Items[LineIndex].OperationType;
    return OperationTypeData[OperationType].DisplayName;
}

void RMaterialOperationNodeViewModel::UseOperationTypeInMenu(int64_t ColumnIndex, int64_t LineIndex)
{
    auto NewOperationType=PopupMenu.Columns[ColumnIndex].Items[LineIndex].OperationType;
    auto CurrentOperationType= OperationModel->GetOperationType();

    auto Operation= NewObject<RMaterialFunctionOperation>();
    auto Model=OperationModel;
    Operation->UndoFunction.Bind([Model,CurrentOperationType](TSharedObjectPtr<RMaterialGraphModel> InGraph)
    {
        Model->SetOperationType(CurrentOperationType);
    });
    Operation->RedoFunction.Bind([Model,NewOperationType](TSharedObjectPtr<RMaterialGraphModel> InGraph)
    {
        Model->SetOperationType(NewOperationType);
    });

    GraphViewModel->RedoOperation(Operation);
    GraphViewModel->AddOperation(Operation);    
}

CString RMaterialOperationNodeViewModel::GetInputPinName(int64_t Index) const
{
    switch(Index)
    {
        case 0:
            return OperationTypeData[OperationModel->GetOperationType()].InputADisplayName;
        case 1:
            return OperationTypeData[OperationModel->GetOperationType()].InputBDisplayName;
        case 2:
            return OperationTypeData[OperationModel->GetOperationType()].InputCDisplayName;
    }

    return U"输入";
}
